﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using Microsoft.Maps.MapControl;
using System.Windows.Media.Imaging;
using System.Linq;

namespace SilverlightMapCreator
{
    public enum ConversionMode
    {
        AffineTransformation,
        TilesSystem
    }
    public static class MapAnalytics
    {
        public static ConversionMode Mode;

        public static Location PixelToLocation(double x,double y)
        {
            if (Mode == ConversionMode.AffineTransformation)
            {
                return null;
            }
            else if (Mode == ConversionMode.TilesSystem)
            {
                return TileSystem.PixelToLocation(x, y);
            }
            else
            {
                throw new Exception("WTF");
            }
        }

        public static List<List<Location>> IncrementalAnalyses(Color selectedColor, WriteableBitmap wBitmap, int colorTolerance)
        {
            
            List<List<Location>> list = new List<List<Location>>();

            bool[] pixelsExplored = new bool[wBitmap.Pixels.Length];
            for (int i = 0; i < wBitmap.Pixels.Length; i++)
            {
                int pixel = wBitmap.Pixels[i];
                Color color = Color.FromArgb(255, (byte)((pixel >> 16) & 0xFF), (byte)((pixel >> 8) & 0xFF), (byte)(pixel & 0xFF));

                if (Math.Abs(color.A - selectedColor.A) < 5 && Math.Abs(color.R - selectedColor.R) < 5 && Math.Abs(color.G - selectedColor.G) < 5 && Math.Abs(color.B - selectedColor.B) < 3)
                {
                    int row = i / wBitmap.PixelWidth;
                    int column = i % wBitmap.PixelHeight;
                    MyPoint MyPoint = new MyPoint(row, column);

                    if (!pixelsExplored[i])
                    {
                        list.Add(new List<Location>());
                        ExploreSorounding(MyPoint, null, selectedColor, list, colorTolerance, true, 2, pixelsExplored, wBitmap,2);
                    }
                }
            }
            return list;
        }

        public static void ExploreSorounding(MyPoint MyPoint, MyPoint previous, Color selectedColor, List<List<Location>> list, int colorParam, bool first, int pointsToMarkAsVisited, bool[] pixelsExplored, WriteableBitmap wBitmap,int param)
        {
            list.Last().Add(PixelToLocation(MyPoint.X, MyPoint.Y));


            int row = (int)MyPoint.Y;
            int column = (int)MyPoint.X;

            pixelsExplored[row * wBitmap.PixelWidth + column] = true;

            for (int i = 1; i <= pointsToMarkAsVisited; i++)
            {
                if ((row * wBitmap.PixelWidth + column - pointsToMarkAsVisited) > 0)
                    pixelsExplored[row * wBitmap.PixelWidth + column - pointsToMarkAsVisited] = true;

                if ((row * wBitmap.PixelWidth + column + pointsToMarkAsVisited) < pixelsExplored.Length)
                    pixelsExplored[row * wBitmap.PixelWidth + column + pointsToMarkAsVisited] = true;

                if ((row * wBitmap.PixelWidth + column + wBitmap.PixelWidth * pointsToMarkAsVisited) < pixelsExplored.Length)
                    pixelsExplored[row * wBitmap.PixelWidth + column + wBitmap.PixelWidth * pointsToMarkAsVisited] = true;

                if ((row * wBitmap.PixelWidth + column - wBitmap.PixelWidth * pointsToMarkAsVisited) > 0)
                    pixelsExplored[row * wBitmap.PixelWidth + column - wBitmap.PixelWidth * pointsToMarkAsVisited] = true;
            }

            List<MyPoint> foundPixels = null;
            if (previous != null)
            {
                int y = (int)(MyPoint.X - previous.X);
                int x = (int)(MyPoint.Y - previous.Y);

                int i = 1;
                while (foundPixels == null || (foundPixels.Count == 0 && i <= 3))
                {
                    i++;
                    foundPixels = SearchPixels(i, i, i, i, row + y, column + x, selectedColor, colorParam,wBitmap,pixelsExplored);
                }
            }

            if (foundPixels == null)
            {
                foundPixels = SearchPixels(param, param, param, param, row, column, selectedColor, colorParam,wBitmap,pixelsExplored);
            }

            MyPoint selectedMyPoint = null;

            var pixels = from pixel in foundPixels
                         let distance = GeoMath.Distance(MyPoint, pixel)
                         orderby distance descending
                         select pixel;

            selectedMyPoint = pixels.FirstOrDefault();

            if (selectedMyPoint != null)
            {
                ExploreSorounding(selectedMyPoint, MyPoint, selectedColor, list, colorParam, false, 2, pixelsExplored, wBitmap,2);
            }

        }


        public static List<MyPoint> SearchPixels(int north, int south, int east, int west, int row, int column, Color selectedColor, int colorParam, WriteableBitmap wBitmap, bool[] pixelsExplored)
        {
            List<MyPoint> foundPixels = new List<MyPoint>();
            if (row - north >= 0 && row + south < wBitmap.PixelHeight && column - east >= 0 && column + west < wBitmap.PixelWidth)
            {
                for (int k = row - north; k < row + south; k++)
                {
                    for (int l = column - east; l < column + west; l++)
                    {
                        int pixel = wBitmap.Pixels[k * wBitmap.PixelWidth + l];
                        Color color = Color.FromArgb(255, (byte)((pixel >> 16) & 0xFF), (byte)((pixel >> 8) & 0xFF), (byte)(pixel & 0xFF));
                        if (Math.Abs(color.R - selectedColor.R) < colorParam && Math.Abs(color.G - selectedColor.G) < colorParam && Math.Abs(color.B - selectedColor.B) < colorParam)
                        {
                            if (!pixelsExplored[k * wBitmap.PixelWidth + l])
                            {
                                foundPixels.Add(new MyPoint(k, l));
                            }
                        }
                    }
                }
            }
            return foundPixels;
        }

        public static List<List<List<Location>>> ConcatenateDisconectedRoutes(List<List<List<Location>>> dictionary, int distanceToConnect)
        {
            List<List<List<Location>>> output = new List<List<List<Location>>>();

            foreach (var bikeRoute in dictionary)
            {
                List<List<Location>> outputRoute = new List<List<Location>>();

                var pairs = from segment in bikeRoute
                            from segment1 in bikeRoute
                            where GeoMath.ComputeDistance(segment.Last(), segment1.First()) < distanceToConnect && segment != segment1
                            select new[] { segment, segment1 };

                foreach (var route in bikeRoute)
                {
                    outputRoute.Add(route);
                }

                if (pairs.Count() > 0)
                {
                    foreach (var pair in pairs)
                    {
                        //Location l1 = pair[0].Last();
                        //Location l2 = pair[1].First();
                        //MapPolyline line = new MapPolyline();
                        //line.Locations = new LocationCollection() { l1, l2 };
                        //line.Stroke = new SolidColorBrush(Colors.Blue);
                        //line.StrokeThickness = 2;
                        //map.Children.Add(line);

                        //Concatenate part
                        outputRoute.Remove(pair[0]);
                        outputRoute.Remove(pair[1]);
                        var concatenated = pair[0].Concat(pair[1]);
                        outputRoute.Add(concatenated.ToList());

                    }
                }


                output.Add(outputRoute);
            }
            return output;
        }

        public static List<List<List<Location>>> ConnectpointsDictionary(List<List<List<Location>>> dictionary,int distanceToConnect)
        {
           List<List<List<Location>>> output = new List<List<List<Location>>>();

            foreach (var bikeRoute in dictionary)
            {
                List<List<Location>> outputRoute = new List<List<Location>>();

                var ordered = bikeRoute.OrderByDescending(x => x.Count).ToList();
                outputRoute.Add(ordered.First());

                for (int i = 1; i < ordered.Count(); i++)
                {
                    //get first point
                    var loc = ordered[i].First();

                    //find nearest segment for all other lines
                    var test = from route in ordered
                               let segment = GeoMath.FindNearestSeg(loc, route)
                               where route != ordered[i]
                               orderby segment.distance ascending
                               select segment;

                    var nearestSegment = test.First();

                    if (nearestSegment.distance < distanceToConnect)
                    {
                        //Location l1 = ordered[i].First();
                        //Location l2 = nearestSegment.nearestPt;
                        //MapPolyline line = new MapPolyline();
                        //line.Locations = new LocationCollection() { l1, l2 };
                        //line.Stroke = new SolidColorBrush(Colors.Red);
                        //line.StrokeThickness = 2;
                        //map.Children.Add(line);

                        ordered[i].Insert(0, nearestSegment.nearestPt);
                        outputRoute.Add(ordered[i]);
                    }
                }
                output.Add(outputRoute);
            }
            return output;
        }
    }
}
